/**
 * @file
 * @brief RTD：XWDS：SOC
 * @author
 * + 隐星魂 (Roy Sun) <xwos@xwos.tech>
 * @copyright
 * + Copyright © 2015 xwos.tech, All Rights Reserved.
 * > Licensed under the Apache License, Version 2.0 (the "License");
 * > you may not use this file except in compliance with the License.
 * > You may obtain a copy of the License at
 * >
 * >         http://www.apache.org/licenses/LICENSE-2.0
 * >
 * > Unless required by applicable law or agreed to in writing, software
 * > distributed under the License is distributed on an "AS IS" BASIS,
 * > WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * > See the License for the specific language governing permissions and
 * > limitations under the License.
 */

#include "board/std.h"
#include <xwos/lib/xwbop.h>
#include <xwos/osal/lock/spinlock.h>
#include <xwcd/ds/soc/chip.h>
#include <xwcd/ds/soc/gpio.h>
#include "Clock_Ip.h"
#include "OsIf.h"
#include "IntCtrl_Ip.h"
#include "Power_Ip.h"
#include "Port_Ci_Port_Ip.h"
#include "Dma_Ip.h"
#include "Gpio_Dio_Ip.h"

struct rtdxwds_soc_drv_data {
        struct {
                struct xwos_splk lock;
        } gpio;
};

struct rtdxwds_soc_cfg {
        struct {
                GPIO_Type * regmap[GPIO_INSTANCE_COUNT];
        } gpio;
};

static
xwer_t rtdxwds_soc_drv_start(struct xwds_device * dev);

static
xwer_t rtdxwds_soc_drv_stop(struct xwds_device * dev);

#if defined(XWCDCFG_ds_PM) && (1 == XWCDCFG_ds_PM)
static
xwer_t rtdxwds_soc_drv_suspend(struct xwds_device * dev);

static
xwer_t rtdxwds_soc_drv_resume(struct xwds_device * dev);
#endif

static
xwer_t rtdxwds_soc_drv_gpio_req(struct xwds_soc * soc,
                                xwid_t port, xwsq_t pinmsk);

static
xwer_t rtdxwds_soc_drv_gpio_rls(struct xwds_soc * soc,
                                xwid_t port, xwsq_t pinmsk);

static
xwer_t rtdxwds_soc_drv_gpio_set(struct xwds_soc * soc,
                                xwid_t port, xwsq_t pinmsk);

static
xwer_t rtdxwds_soc_drv_gpio_reset(struct xwds_soc * soc,
                                  xwid_t port, xwsq_t pinmsk);

static
xwer_t rtdxwds_soc_drv_gpio_toggle(struct xwds_soc * soc,
                                   xwid_t port, xwsq_t pinmsk);

static
xwer_t rtdxwds_soc_drv_gpio_output(struct xwds_soc * soc,
                                   xwid_t port, xwsq_t pinmsk,
                                   xwsq_t out);

static
xwer_t rtdxwds_soc_drv_gpio_input(struct xwds_soc * soc,
                                  xwid_t port, xwsq_t pinmsk,
                                  xwsq_t * in);

const struct xwds_soc_driver rtdxwds_soc_drv = {
        .base = {
                .name = "rtdxwds.soc",
                .probe = NULL,
                .remove = NULL,
                .start = rtdxwds_soc_drv_start,
                .stop = rtdxwds_soc_drv_stop,
#if defined(XWCDCFG_ds_PM) && (1 == XWCDCFG_ds_PM)
                .suspend = rtdxwds_soc_drv_suspend,
                .resume = rtdxwds_soc_drv_resume,
#endif
        },

        .gpio_req = rtdxwds_soc_drv_gpio_req,
        .gpio_rls = rtdxwds_soc_drv_gpio_rls,
        .gpio_cfg = NULL,
        .gpio_set = rtdxwds_soc_drv_gpio_set,
        .gpio_reset = rtdxwds_soc_drv_gpio_reset,
        .gpio_toggle = rtdxwds_soc_drv_gpio_toggle,
        .gpio_output = rtdxwds_soc_drv_gpio_output,
        .gpio_input = rtdxwds_soc_drv_gpio_input,
};

const struct rtdxwds_soc_cfg rtdxwds_soc_cfg = {
        .gpio = {
                .regmap = {
                        [XWDS_GPIO_PORT_A] = IP_PTA,
                        [XWDS_GPIO_PORT_B] = IP_PTB,
                        [XWDS_GPIO_PORT_C] = IP_PTC,
                        [XWDS_GPIO_PORT_D] = IP_PTD,
                        [XWDS_GPIO_PORT_E] = IP_PTE,
                },
        },
};

atomic_xwsq_t rtdxwds_gpio_pin_state[] = {
        [XWDS_GPIO_PORT_A] = 0,
        [XWDS_GPIO_PORT_B] = 0,
        [XWDS_GPIO_PORT_C] = 0,
        [XWDS_GPIO_PORT_D] = 0,
        [XWDS_GPIO_PORT_E] = 0,
};

struct rtdxwds_soc_drv_data rtdxwds_soc_drv_data;

struct xwds_soc rtdxwds_soc = {
        /* attributes */
        .dev = {
                .name = "rtdxwds.soc",
                .id = 0,
                .resources = NULL,
                .drv = xwds_cast(struct xwds_driver *, &rtdxwds_soc_drv),
                .data = &rtdxwds_soc_drv_data,
        },
        .xwccfg = (void *)&rtdxwds_soc_cfg,
        .gpio = {
                .cfg = NULL,
                .pins = rtdxwds_gpio_pin_state,
                .port_num = xw_array_size(rtdxwds_gpio_pin_state),
                .pin_num = 32,
        },
};

static
xwer_t rtdxwds_soc_drv_start(struct xwds_device * dev)
{
        struct rtdxwds_soc_drv_data * drvdata;

        drvdata = dev->data;
        xwos_splk_init(&drvdata->gpio.lock);

        /* Init Clock */
        Clock_Ip_StatusType Status_Clock = CLOCK_IP_ERROR;

        Status_Clock = Clock_Ip_Init(&Clock_Ip_aClockConfig[0]);
        XWOS_BUG_ON(Status_Clock != CLOCK_IP_SUCCESS);
        while (CLOCK_IP_PLL_LOCKED != Clock_Ip_GetPllStatus()) {
                /* Busy wait until the System PLL is locked */
        }
        Clock_Ip_DistributePll();

        /* Init OsIf */
        OsIf_Init(NULL_PTR);

        /* Init IntCtrl */
        IntCtrl_Ip_StatusType Status_IntCtrl = INTCTRL_IP_STATUS_ERROR;

        Status_IntCtrl = IntCtrl_Ip_Init(&IntCtrlConfig_0);
        XWOS_BUG_ON(Status_IntCtrl != INTCTRL_IP_STATUS_SUCCESS);

        Status_IntCtrl = IntCtrl_Ip_ConfigIrqRouting(&intRouteConfig);
        XWOS_BUG_ON(Status_IntCtrl != INTCTRL_IP_STATUS_SUCCESS);

        /* Init Power */
        Power_Ip_Init(&Power_Ip_HwIPsConfigPB);
        Power_Ip_SetMode(&Power_Ip_aModeConfigPB[0]);

        /* Init GPIO */
        Port_Ci_Port_Ip_Init(NUM_OF_CONFIGURED_PINS0, g_pin_mux_InitConfigArr0);

        /* Init DMA */
        Dma_Ip_Init(&Dma_Ip_xDmaInitPB);

        return XWOK;
}

static
xwer_t rtdxwds_soc_drv_stop(struct xwds_device * dev)
{
        XWOS_UNUSED(dev);
        return XWOK;
}

#if defined(XWCDCFG_ds_PM) && (1 == XWCDCFG_ds_PM)
static
xwer_t rtdxwds_soc_drv_suspend(struct xwds_device * dev)
{
        XWOS_UNUSED(dev);
        return XWOK;
}

static
xwer_t rtdxwds_soc_drv_resume(struct xwds_device * dev)
{
        XWOS_UNUSED(dev);

        /* Init Clock */
        Clock_Ip_StatusType Status_Clock = CLOCK_IP_ERROR;

        Status_Clock = Clock_Ip_Init(&Clock_Ip_aClockConfig[0]);
        XWOS_BUG_ON(Status_Clock != CLOCK_IP_SUCCESS);
        return XWOK;
}
#endif

static
xwer_t rtdxwds_soc_drv_gpio_req(struct xwds_soc * soc,
                                xwid_t port, xwsq_t pinmsk)
{
        XWOS_UNUSED(soc);
        XWOS_UNUSED(port);
        XWOS_UNUSED(pinmsk);
        return XWOK;
}

static
xwer_t rtdxwds_soc_drv_gpio_rls(struct xwds_soc * soc,
                                xwid_t port, xwsq_t pinmsk)
{
        XWOS_UNUSED(soc);
        XWOS_UNUSED(port);
        XWOS_UNUSED(pinmsk);
        return XWOK;
}

static
xwer_t rtdxwds_soc_drv_gpio_set(struct xwds_soc * soc,
                                xwid_t port, xwsq_t pinmsk)
{
        const struct rtdxwds_soc_cfg * soccfg;
        struct rtdxwds_soc_drv_data * drvdata;
        GPIO_Type * gpio;
        xwreg_t cpuirq;
        xwer_t rc;

        if (port < GPIO_INSTANCE_COUNT) {
                drvdata = soc->dev.data;
                soccfg = soc->xwccfg;
                gpio = soccfg->gpio.regmap[port];
                xwos_splk_lock_cpuirqsv(&drvdata->gpio.lock, &cpuirq);
                Gpio_Dio_Ip_SetPins(gpio, (xwu32_t)pinmsk);
                xwos_splk_unlock_cpuirqrs(&drvdata->gpio.lock, cpuirq);
                rc = XWOK;
        } else {
                rc = -EINVAL;
        }
        return rc;
}

static
xwer_t rtdxwds_soc_drv_gpio_reset(struct xwds_soc * soc,
                                  xwid_t port, xwsq_t pinmsk)
{
        const struct rtdxwds_soc_cfg * soccfg;
        struct rtdxwds_soc_drv_data * drvdata;
        GPIO_Type * gpio;
        xwreg_t cpuirq;
        xwer_t rc;

        if (port < GPIO_INSTANCE_COUNT) {
                drvdata = soc->dev.data;
                soccfg = soc->xwccfg;
                gpio = soccfg->gpio.regmap[port];
                xwos_splk_lock_cpuirqsv(&drvdata->gpio.lock, &cpuirq);
                Gpio_Dio_Ip_ClearPins(gpio, (xwu32_t)pinmsk);
                xwos_splk_unlock_cpuirqrs(&drvdata->gpio.lock, cpuirq);
                rc = XWOK;
        } else {
                rc = -EINVAL;
        }
        return rc;
}

static
xwer_t rtdxwds_soc_drv_gpio_toggle(struct xwds_soc * soc,
                                   xwid_t port, xwsq_t pinmsk)
{
        const struct rtdxwds_soc_cfg * soccfg;
        struct rtdxwds_soc_drv_data * drvdata;
        GPIO_Type * gpio;
        xwreg_t cpuirq;
        xwer_t rc;

        if (port < GPIO_INSTANCE_COUNT) {
                drvdata = soc->dev.data;
                soccfg = soc->xwccfg;
                gpio = soccfg->gpio.regmap[port];
                xwos_splk_lock_cpuirqsv(&drvdata->gpio.lock, &cpuirq);
                Gpio_Dio_Ip_TogglePins(gpio, (xwu32_t)pinmsk);
                xwos_splk_unlock_cpuirqrs(&drvdata->gpio.lock, cpuirq);
                rc = XWOK;
        } else {
                rc = -EINVAL;
        }
        return rc;
}

static
xwer_t rtdxwds_soc_drv_gpio_output(struct xwds_soc * soc,
                                   xwid_t port, xwsq_t pinmsk,
                                   xwsq_t out)
{
        const struct rtdxwds_soc_cfg * soccfg;
        struct rtdxwds_soc_drv_data * drvdata;
        GPIO_Type * gpio;
        xwreg_t cpuirq;
        xwer_t rc;

        if (port < GPIO_INSTANCE_COUNT) {
                drvdata = soc->dev.data;
                soccfg = soc->xwccfg;
                gpio = soccfg->gpio.regmap[port];
                xwos_splk_lock_cpuirqsv(&drvdata->gpio.lock, &cpuirq);
                Gpio_Dio_Ip_WritePins(gpio, (pinmsk & out));
                xwos_splk_unlock_cpuirqrs(&drvdata->gpio.lock, cpuirq);
                rc = XWOK;
        } else {
                rc = -EINVAL;
        }
        return rc;
}

static
xwer_t rtdxwds_soc_drv_gpio_input(struct xwds_soc * soc,
                                  xwid_t port, xwsq_t pinmsk,
                                  xwsq_t * in)
{
        const struct rtdxwds_soc_cfg * soccfg;
        struct rtdxwds_soc_drv_data * drvdata;
        GPIO_Type * gpio;
        Gpio_Dio_Ip_PinsChannelType pins;
        xwreg_t cpuirq;
        xwer_t rc;

        if (port < GPIO_INSTANCE_COUNT) {
                drvdata = soc->dev.data;
                soccfg = soc->xwccfg;
                gpio = soccfg->gpio.regmap[port];
                xwos_splk_lock_cpuirqsv(&drvdata->gpio.lock, &cpuirq);
                pins = Gpio_Dio_Ip_ReadPins(gpio);
                xwos_splk_unlock_cpuirqrs(&drvdata->gpio.lock, cpuirq);
                *in = pins & pinmsk;
                rc = XWOK;
        } else {
                rc = -EINVAL;
        }
        return rc;
}
